<!DOCTYPE html>
<html lang="es">
    <head>
        <title>Introducción practica a Qt con Python</title>
        <meta charset="utf-8">
        <meta name="description"
              content="Éste tutorial muestra como crear un reproductor multimedia
                       mientras aprendes las bases de la programacion con el framework Qt
                       bajo el lenguaje de programación Python." />
        <meta name="keywords"
              content="Python,Qt,tutorial,programacion,reproductor multimedia" />
        <meta name="author"
              content="Gonzalo Exequiel Pedone" />
        <link type="text/css"
              rel="stylesheet"
              href="syntaxhighlighter/styles/shCoreDefault.css" />
        <link rel="stylesheet"
              href="styles/print.css"
              type="text/css"
              media="print" />
        <link rel="stylesheet"
              href="styles/screen.css"
              type="text/css"
              media="screen" />
        <script type="text/javascript"
                src="syntaxhighlighter/scripts/shCore.js"></script>
        <script type="text/javascript"
                src="syntaxhighlighter/scripts/shBrushPython.js"></script>
        <script type="text/javascript"
                src="scripts/highlighter.js"></script>
    </head>
    <body>
        <div class="page">
            <div class="title">
                Introducción practica a Qt con Python
            </div>
            <div class="license">
                <a target="_blank"
                   rel="license"
                   href="http://creativecommons.org/licenses/by-sa/3.0/">
                    <img class="thumbnail"
                         alt="Licencia Creative Commons"
                         src="pics/cc3.0-by-sa-88x31.png" />
                </a><br />
                <span xmlns:dct="http://purl.org/dc/terms/"
                      href="http://purl.org/dc/dcmitype/Text"
                      property="dct:title"
                      rel="dct:type">
                    Introducción practica a Qt con Python
                </span> por
                <a target="_blank"
                   href="http://hipersayanx.blogspot.com/"
                   xmlns:cc="http://creativecommons.org/ns#"
                   property="cc:attributionName"
                   rel="cc:attributionURL">
                    Gonzalo Exequiel Pedone</a>
                esta bajo una licencia
                <a target="_blank"
                   rel="license"
                   href="http://creativecommons.org/licenses/by-sa/3.0/">
                    Creative Commons Reconocimiento-Compartir Igual 3.0 Unported
                </a>.
            </div>
        </div>
        <div class="page">
            <h1>Índice del contenido</h1> <!-- Y si solamente pongo "indice"? -->
            <ul>
                <li>
                    <a href="#intro">
                        Introducción
                    </a>
                </li>
                <li>
                    <a href="#tools">
                        Las herramientas
                    </a>
                </li>
                <li>
                    <a href="#designer">
                        Diseñando la interfaz gráfica del usuario
                    </a>
                </li>
                <li>
                    <a href="#code">
                        El código
                    </a>
                </li>
                <li>
                    <a href="#menubuttons">
                        Botones de menú
                    </a>
                </li>
                <li>
                    <a href="#signalsandslots">
                        Señales y ranuras
                    </a>
                </li>
                <li>
                    <a href="#addfilesplaylist">
                        Agregar archivos a una lista de reproducción
                    </a>
                </li>
                <li>
                    <a href="#playfiles">
                        Reproducir archivos
                    </a>
                </li>
                <li>
                    <a href="#timers">
                        Usando temporizadores
                    </a>
                </li>
                <li>
                    <a href="#fullscreen">
                        ¡A pantalla completa!
                    </a>
                </li>
                <li>
                    <a href="#aspectratio">
                        Relación de aspecto del vídeo
                    </a>
                </li>
                <li>
                    <a href="#playpause">
                        Reproducir y pausar
                    </a>
                </li>
                <li>
                    <a href="#shufflerepeat">
                        Aleatorizar y repetir la lista de reproducción
                    </a>
                </li>
                <li>
                    <a href="#seekmedia">
                        Mover hacia adelante o hacia atrás la reproducción multimedia
                    </a>
                </li>
                <li>
                    <a href="#clearplaylist">
                        Removiendo archivos multimedia de la lista de reproducción
                    </a>
                </li>
                <li>
                    <a href="#addurlplaylist">
                        Agregar URLs a la lista de reproducción
                    </a>
                </li>
                <li>
                    <a href="#about">
                        El dialogo "Acerca de"
                    </a>
                </li>
                <li>
                    <a href="#stylesheets">
                        Usando hojas de estilo (stylesheets)
                    </a>
                </li>
                <li>
                    <a href="#internationalisation">
                        Internacionalización
                    </a>
                </li>
                <li>
                    <a href="#relatedlinks">
                        Enlaces de interés
                    </a>
                </li>
            </ul>
        </div>
        <div class="page">
            <h1><a name="intro">Introducción</a></h1>
            <p>
                Esta guía pretende ser una breve introducción a la programación
                de interfaces gráficas para usuarios (GUI por sus siglas en inglés),
                usando el lenguaje de programación Python y el framework Qt, ambos multiplataforma.
            </p>
            <p>
                En esta guía usaremos PyQt para mostrar las bases de la programación
                en Qt con Python.
            </p>
            <p>
                Para hacer ésta guía mas divertida, porqué usted podría aprender
                el lado teórico de la programación con Qt sólo leyendo la documentación,
                Me concentrare sólo en la parte practica, por lo tanto he decidido
                mostrar como crear un reproductor de vídeo, cuya implementación es increíblemente
                sencilla con Qt. Para una mejor compresión a lo que me refiero,
                mire la imagen de abajo:
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot001.png">
                    <img class="thumbnail-75"
                         alt="final result"
                         src="pics/snapshots/snapshot001.png" />
                </a>
            </p>
            <p>
                Si esto le parece interesante, continué leyendo.
            </p>
        </div>
        <div class="page">
            <h1><a name="tools">
                Las herramientas
            </a></h1>
            <p>
                El primer paso es descargar todas las herramientas que necesitaremos
                para crear nuestros programas con PyQt.
            </p>
            <p>
                Está guía esta pensada para personas que ya tienen conocimientos
                acerca de la programación con Python, puedes descargar el interprete
                aquí si aun no lo tiene instalado:
            </p>
            <p class="space"></p>
            <p>
                <a target="_blank"
                   href="http://www.python.org/download/">
                    www.python.org/download
                </a>
            </p>
            <p class="space"></p>
            <p>
                Ahora necesitamos descargar los bindings de Qt para usarlos
                dentro de Python, usaremos PyQt. Puede descargarlo en:
            </p>
            <p class="space"></p>
            <p>
                <a target="_blank"
                   href="http://www.riverbankcomputing.co.uk/software/pyqt/download">
                    www.riverbankcomputing.co.uk/software/pyqt/download
                </a>
            </p>
            <p class="space"></p>
            <p>
                En esta guía decidí usar <a target="_blank"
                href="http://ninja-ide.org/">Ninja IDE</a> como entorno
                para desarrollo, así haremos el desarrollo ágil y consistente
                entre varios entornos de trabajo (sistemas operativos,
                entornos gráficos, etc). Descarga Ninja IDE en:
            </p>
            <p class="space"></p>
            <p>
                <a target="_blank"
                   href="http://ninja-ide.org/downloads.html">
                    ninja-ide.org/downloads.html
                </a>
            </p>
            <p class="space"></p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot002.png">
                    <img class="thumbnail-75"
                         alt="Ninja IDE"
                         src="pics/snapshots/snapshot002.png" />
                </a>
            </p>
        </div>
        <div class="page">
            <h1><a name="designer">
                Diseñando la interfaz gráfica del usuario
            </a></h1>
            <p>
                Comencemos con la parte fácil, el diseño de la GUI. Vamos a usar
                Qt Designer el cual viene con el paquete de instalación de PyQt
                para Mac y Windows, o como dependencia en GNU/Linux.
            </p>
            <p>
                Debemos abrir Qt Designer desde el menú de inicio.
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot003.png">
                    <img class="thumbnail"
                         alt="Open Qt Designer"
                         src="pics/snapshots/snapshot003.png" />
                </a>
            </p>
            <p>
                Cuando abres Qt Designer, le aparecera un dialogo para seleccionar el
                tipo de ventana que usara nuestro programa, dependiendo del tipo
                seleccionado, el manejador de ventanas (window manager) del entorno
                gráfico, tratara la ventana del programa de una forma u otra,
                tenemos tres tipos de ventanas para escoger:
            </p>
            <ul>
                <li>
                    <strong>Ventana Principal (Main Window)</strong>: Como su nombre indica, tiene
                    propiedades especiales que hacen que sea tratada como tal
                    por el manejador de ventanas (window manager).
                </li>
                <li>
                    <strong>Dialogo (Dialog)</strong>: Más indicado para usarse como hijo
                    (children) de otra ventana, por ejemplo , como diálogos de configuración,
                    diálogos "Acerca de", etc...
                </li>
                <li>
                    <strong>Widget</strong>: Esté puede ser una ventana o un elemento gráfico
                    genérico, aproximadamente, un widget es un elemento gráfico que interactúa
                    con el usuario, por ejemplo, botones, cajas de selección, cajas de texto,
                    etc... A su vez un widget puede ser un contenedor para otros widgets, y un
                    widget sin un contenedor <!--- a foreveralone widget? ---> es una
                    ventana independiente, en este caso, este widget estaría siendo tratada como
                    una ventana genérica.
                </li>
            </ul>
        </div>
        <div class="page">
            <p>
                En este caso seleccionaremos "Main Window".
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot004.png">
                    <img class="thumbnail-75"
                         alt="Select Main Window"
                         src="pics/snapshots/snapshot004.png" />
                </a>
            </p>
            <p>
                Aquí creamos una interfaz lo más simple posible, con acceso
                directo a las opciones más comunes, para ganar mas espacio borraremos
                la barra de menú y la barra de estado.
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot005.png">
                    <img class="thumbnail-75"
                         alt="Remove menu and status bar"
                         src="pics/snapshots/snapshot005.png" />
                </a>
            </p>
        </div>
        <div class="page">
            <p>
                Ahora comenzamos arrastrando los widgets desde el Widget Box y
                los soltamos dentro de la ventana.
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot006.png">
                    <img class="thumbnail-75"
                         alt="Drag&and;drop widgets"
                         src="pics/snapshots/snapshot006.png" />
                </a>
            </p>
            <p>
                En general, las opciones más populares que todo reproductor de vídeo
                tiene (en mi opinión):
            </p>
            <ul>
                <li>
                    Un área para mostrar el vídeo
                    (<strong>Phonon::VideoPlayer</strong>).
                </li>
                <li>
                    Una barra que muestra la lista de reproducción y sus opciones
                    (<strong>Dock Widget</strong>).
                </li>
                <li>
                    Botones Reproducción/Pausa, Anterior y Siguiente
                    (<strong>Push Butrón</strong>).
                </li>
                <li>
                    Un control para ajustar el volumen y otro para avanzar o
                    retroceder la reproducción manualmente
                    (<strong>phonon::VolumeSlider</strong> y
                    <strong>phonon::SeekSlider</strong>).
                </li>
                <li>
                    Un área para mostrar el tiempo de reproducción
                    (<strong>Label</strong>).
                </li>
                <li>
                    Un botón para que el reproductor se muestre en pantalla completa
                </li>
                <li>
                    Un botón para cambiar el espacio ocupado por el vídeo junto con
                    el widget de vídeo. <!--- Sinceramente no comprendí bien está --->
                </li>
                <li>
                    Un botón para esconder o mostrar la lista de reproducción.
                </li>
                <li>
                    Y finalmente <!--- Gracias a Dios! --->el botón "Acerca de".
                </li>
            </ul>
            <p>
                Coloca todos estos controles (excepto
                <strong>Phonon::VideoPlayer</strong>) Dentro de un
                <strong>Widget</strong>. Debería de lucir similar a esto:
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot007.png">
                    <img class="thumbnail-75"
                         alt="Widgets position"
                         src="pics/snapshots/snapshot007.png" />
                </a>
            </p>
        </div>
        <div class="page">
            <p>
                Como puede ver, he cambiado el texto de los botones y las etiquetas.
                Puede cambiar el texto con un doble clic sobre él y escribir
                el nuevo texto. El titulo de la lista de reproducción es cambiado
                con la propiedad <strong>windowTitle</strong>.
            </p>
            <p>
                Ahora vamos a probar la interfaz, para activar la previsualización
                presione CTRL+R. En la previsualización podemos interactuar con la
                ventana y sus widgets de la misma manera que lo haríamos con un
                programa ya terminado, podemos presionar botones, mover deslizadores,
                mover el dock<!--- Banquillo? glol --->, redimensionar la ventana, etc.,
                Por supuesto, la previsualización no posee todas las funcionalidades que usted
                podría esperar de un programa terminado.
            </p>
            <p>
                Si redimensiona la ventana, los widgets permanecerán fijas,
                y su posición y su tamaño no calza el tamaño de la ventana, excepto
                el dock.
            </p>
            <p>
                Para arreglar esté problema, clic derecho en cualquier área vacía
                que contenga los widgets -&gt; Lay out -&gt; Lay out in a grid. <!--- supongo que hay que poner la traduccion en español... --->
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot008.png">
                    <img class="thumbnail-75"
                         alt="Lay out in a grid"
                         src="pics/snapshots/snapshot008.png" />
                </a>
            </p>
            <p>
                Ahora vamos a diseñar la barra de la lista de reproducción.
                De la misma forma como hicimos con la ventana, arrastrando y
                soltando los widgets dentro del Dock.
            </p>
            <p>
                Los widgets que usamos son:
            </p>
            <ul>
                <li>
                    <strong>Widget Lista (List Widget)</strong>, aquí se muestra todos los archivos y
                    URLs para reproducir.
                </li>
                <li>
                    Un botón para agregar nuevos archivos multimedia, podemos agregar archivos locales, además
                    direcciones web de radio y televisión sobre la Internet (transmisiones en-linea).
                </li>
                <li>
                    Un botón para Aleatorizar y otro más para repetir la lista de reproducción.
                </li>
                <li>
                    Un botón para remover un archivo multimedia y otro para limpiar
                    la lista de reproducción entera.
                </li>
                <li>
                    También añadimos dos <strong>espaciadores horizontales</strong> para separar
                    y mantener ordenado los diferentes grupos de características.
                </li>
            </ul>
        </div>
        <div class="page">
            <p>
                Finalmente, acomodamos los widgets en una grilla como hicimos antes, tal
                como en la imagen siguiente:
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot009.png">
                    <img class="thumbnail-75"
                         alt="Final widgets position"
                         src="pics/snapshots/snapshot009.png" />
                </a>
            </p>
            <p>
                Ahora vamos a hacer la interfaz más intuitiva y menos dependiente
                del lenguaje, en lugar de usar palabras, letras o símbolos en los
                botones, usaremos iconos.
            </p>
            <p>
                Para esto, has doble clic en cada botón y borra el texto,
                luego en la barra de propiedades (property bar), cambia la
                propiedad <strong>maximumSize</strong> a 24x24, y en la propiedad
                <strong>icon</strong>, presiona en la flecha hacia abajo y selecciona
                "Choose File...".
            </p>
            <p>
                El orden de los iconos de izquierda a derecha es:
            </p>
            <table>
                <thead>
                    <th class="texttoleft">
                        Nombre
                    </th>
                    <th>
                        Icono
                    </th>
                    <th>
                        Alternativa
                    </th>
                    <th>
                        Seleccionable
                    </th>
                </thead>
                <tbody>
                    <tr>
                        <td>
                            Reproducir/Pausar
                        </td>
                        <td class="centercell">
                            <img class="thumbnail"
                                 alt="Play"
                                 src="../share/icons/media-playback-start.png">
                        </td>
                        <td class="centercell">
                            <img class="thumbnail"
                                 alt="Pause"
                                 src="../share/icons/media-playback-pause.png">
                        </td>
                        <td class="centercell">
                            <strong>Sí</strong>
                        </td>
                    </tr>
                    <tr>
                        <td>
                            Detener
                        </td>
                        <td class="centercell">
                            <img class="thumbnail"
                                 alt="Pause"
                                 src="../share/icons/media-playback-stop.png">
                        </td>
                        <td class="centercell">
                        </td>
                        <td class="centercell">
                            No
                        </td>
                    </tr>
                    <tr>
                        <td>
                            Saltar hacia atrás
                        </td>
                        <td class="centercell">
                            <img class="thumbnail"
                                 alt="Skip backward"
                                 src="../share/icons/media-skip-backward.png">
                        </td>
                        <td class="centercell">
                        </td>
                        <td class="centercell">
                            No
                        </td>
                    </tr>
                    <tr>
                        <td>
                            Saltar hacia adelante
                        </td>
                        <td class="centercell">
                            <img class="thumbnail"
                                 alt="Skip forward"
                                 src="../share/icons/media-skip-forward.png">
                        </td>
                        <td class="centercell">
                        </td>
                        <td class="centercell">
                            No
                        </td>
                    </tr>
                    <tr>
                        <td>
                            Pantalla completa
                        </td>
                        <td class="centercell">
                            <img class="thumbnail"
                                 alt="Fullscreen"
                                 src="../share/icons/view-fullscreen.png">
                        </td>
                        <td class="centercell">
                            <img class="thumbnail"
                                 alt="View restore"
                                 src="../share/icons/view-restore.png">
                        </td>
                        <td class="centercell">
                            <strong>Sí</strong>
                        </td>
                    </tr>
                    <tr>
                        <td>
                            Acerca de...
                        </td>
                        <td class="centercell">
                            <img class="thumbnail"
                                 alt="About"
                                 src="../share/icons/help-about.png">
                        </td>
                        <td class="centercell">
                        </td>
                        <td class="centercell">
                            No
                        </td>
                    </tr>
                    <tr>
                        <td>
                            Agregar audiovisual
                        </td>
                        <td class="centercell">
                            <img class="thumbnail"
                                 alt="Add media"
                                 src="../share/icons/list-add.png">
                        </td>
                        <td class="centercell">
                        </td>
                        <td class="centercell">
                            No
                        </td>
                    </tr>
                    <tr>
                        <td>
                            Lista de reproducción aleatoria
                        </td>
                        <td class="centercell">
                            <img class="thumbnail"
                                 alt="Playlist shuffle"
                                 src="../share/icons/media-playlist-shuffle.png">
                        </td>
                        <td class="centercell">
                        </td>
                        <td class="centercell">
                            <strong>Sí</strong>
                        </td>
                    </tr>
                    <tr>
                        <td>
                            Repetir lista de reproducción
                        </td>
                        <td class="centercell">
                            <img class="thumbnail"
                                 alt="Playlist repeat"
                                 src="../share/icons/media-playlist-repeat.png">
                        </td>
                        <td class="centercell">
                        </td>
                        <td class="centercell">
                            <strong>Sí</strong>
                        </td>
                    </tr>
                    <tr>
                        <td>
                            Remover audiovisuales seleccionados
                        </td>
                        <td class="centercell">
                            <img class="thumbnail"
                                 alt="Remove media"
                                 src="../share/icons/list-remove.png">
                        </td>
                        <td class="centercell">
                        </td>
                        <td class="centercell">
                            No
                        </td>
                    </tr>
                    <tr>
                        <td>
                            Limpiar lista de reproducción
                        </td>
                        <td class="centercell">
                            <img class="thumbnail"
                                 alt="Playlist clear"
                                 src="../share/icons/edit-clear.png">
                        </td>
                        <td class="centercell">
                        </td>
                        <td class="centercell">
                            No
                        </td>
                    </tr>
                </tbody>
            </table>
            <p>
                Usted puede configurar el icono del botón con la propiedad
                <strong>icon</strong>. La propiedad <strong>icon</strong>
                ofrece muchos estilos de iconos dependiendo del estado del
                estado del botón, pero solo dos estados son de nuestro interés:
            </p>
            <ul>
                <li>
                    <strong>Normal Off</strong>: Este es el estado de nuestro botón cuando
                    no es presionado o seleccionado.
                </li>
                <li>
                    <strong>Normal On</strong>: Este es el estado de nuestro botón
                    cuando es presionado o seleccionado. Aquí configuraremos el icono
                    alternativo.
                </li>
            </ul>
        </div>
        <div class="page">
            <p>
                Ahora vamos a configurar la apariencia de la lista de reproducción.
            </p>
            <p>
                En la propiedad <strong>features</strong>, marcamos
                <em>DockWidgetMovable</em> y <em>DockWidgetFlotable</em> y
                desmarcamos el resto, estas propiedades indican que nosotros podemos mover y
                separar la lista de reproducción, respectivamente, dentro y fuera de la ventana,
                pero no cerrarla o cambiar su apariencia, entre otras cosas.
            </p>
            <p>
                En la propiedad <strong>allowedAreas</strong>, marcamos
                <em>LeftDockWidgetArea</em> y <em>RightDockWidgetArea</em> y
                desmarcamos el resto, estas propiedades indican que nosotros solo podemos
                mover la lista de reproducción hacia la derecha y hacia la izquierda
                respectivamente dentro de la ventana, pero nunca hacia la parte superior
                o inferior.
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot010.png">
                    <img class="thumbnail-75"
                         alt="Widget properties"
                         src="pics/snapshots/snapshot010.png" />
                </a>
            </p>
        </div>
        <div class="page">
            <p>
                La interfaz de nuestro reproductor de vídeo esta casi terminada, solo
                necesitamos renombrar los widgets para acceder a ellos fácilmente desde
                el código fuente. Para ello, haz clic en el widget que deseas cambiar de
                nombre, y en la barra "Inspector de objetos" (Object Inspector bar), doble
                clic en el objeto seleccionado para cambiar su nombre.
            </p>
            <p>
                Usualmente, los nombres de los widgets empiezan con tres letras
                indicando el tipo de widget, seguido del por el nombre o acción
                realizada por el widget, donde cada palabra es escrita en mayúscula,
                por ejemplo:
            </p>
            <p class="space"></p>
            <p>
                <em>btn</em><strong>PlayPause</strong> → <em>btn</em> = Botón,
                <strong>PlayPause</strong> = Play o Pause (Reproduccion o Pausa).
            </p>
            <p class="space"></p>
            <p>
                En éste tutorial los prefijos usados son:
            </p>
            <ul>
                <li>
                    btn → Botón
                </li>
                <li>
                    cbx → Combo Box
                </li>
                <li>
                    lbl → Label (etiqueta)
                </li>
                <li>
                    sld → Slider (deslizador)
                </li>
                <li>
                    vdp → Video Player (Video reproductor)
                </li>
                <li>
                    hsp → Horizontal Spacer (Espaciador Horizontal)
                </li>
                <li>
                    lsw → List Widget (Widget Lista)
                </li>
            </ul>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot011.png">
                    <img class="thumbnail-50"
                         alt="Object Inspector"
                         src="pics/snapshots/snapshot011.png" />
                </a>
            </p>
            <p>
                Y finalmente podemos probar nuestra GUI acabada presionando Ctrl+R.
            </p>
        </div>
        <div class="page">
            <h1><a name="code">
                El código
            </a></h1>
            <p>
                Once finished the GUI design, we must complete our program
                functionality through the code.
            </p>
            <p>
                Since this tutorial is intended to cover as many users on
                multiple operating systems, and also to standardize a bit the
                development process, we will use IDE Ninja to develop the full
                program.
            </p>
            <p>
                So to start, open Ninja IDE.
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot012.png">
                    <img class="thumbnail"
                         alt="Open Ninja IDE"
                         src="pics/snapshots/snapshot012.png" />
                </a>
            </p>
            <p>
                As soon Ninja IDE is opened, it shows a nice welcome screen,
                press the second icon in the toolbar to create a new project.
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot013.png">
                    <img class="thumbnail"
                         alt="Ninja IDE toolbar"
                         src="pics/snapshots/snapshot013.png" />
                </a>
            </p>
        </div>
        <div class="page">
            <p>
                Select the option to create a Python project.
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot014.png">
                    <img class="thumbnail"
                         alt="Select project type"
                         src="pics/snapshots/snapshot014.png" />
                </a>
            </p>
            <p>
                Introduce the program data and press <b>Finish</b>. Fields
                marked with an asterisk (*) are required fields.
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot015.png">
                    <img class="thumbnail"
                         alt="Fill project data"
                         src="pics/snapshots/snapshot015.png" />
                </a>
            </p>
        </div>
        <div class="page">
            <p>
                When we finish creating the project, the project folder and a
                file <em>__ init__.py</em> file appear in the project bar.
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot016.png">
                    <img class="thumbnail"
                         alt="Remove __ init__.py file"
                         src="pics/snapshots/snapshot016.png" />
                </a>
            </p>
            <p>
                But, what is that <em>__ init__.py</em> file?
            </p>
            <p>
                Files with the <em>__init__.py</em> name are used to mark the
                directories on the disk as a Python package directory. If you
                have the files:
            </p>
            <pre>
mydir/spam/__init__.py
mydir/spam/module.py
            </pre>
            <p>
                and <em>mydir</em> is the working path of your project, then you
                can import the module <em>module.py</em> as:
            </p>
            <pre class="brush: py;">
import spam.module
            </pre>
            <p>
                or
            </p>
            <pre class="brush: py;">
from spam import module
            </pre>
            <p>
                If you delete the <em>__init__.py</em>, Python will no look for
                submodules in that directory, so Python will fail when trying to
                import that module.
            </p>
            <p>
                The <em>__init__.py</em> file is usually empty, but can be used
                to export selected parts of the package under certain names,
                store certain functions, etc.. Considering the above example,
                the contents of the <em>__init__.py</em> module can be accessed
                as:
            </p>
            <pre class="brush: py;">
import spam
            </pre>
            <p>
                Related link:
            </p>
            <p class="space"></p>
            <p>
                <a target="_blank"
                   href="http://effbot.org/pyfaq/what-is-init-py-used-for.htm">
                    effbot.org/pyfaq/what-is-init-py-used-for.htm
                </a>
            </p>
            <p class="space"></p>
            <p>
                In this tutorial we will not use the <em>__init__.py</em> files,
                so we can delete them.
            </p>
            <p>
                Now let's build the directory tree of the project. For instance,
                start by creating the <em>src</em> folder where will put the
                source code of the program, to do this right click on the
                project folder, and select <strong>Add New Folder</strong>.
            </p>
            <p>
                Write the name of the folder and press <strong>OK</strong>.
            </p>
            <p>
                To create a subpath, right click on the folder where you want to
                create a subpath and select the corresponding option.
            </p>
        </div>
        <div class="page">
            <p>
                The directory tree of the project should be as follows:
            </p>
            <ul>
                <li>
                    <em>PythonQtVideoPlayer/share/icons</em> → Here we place
                    all the program icons.
                </li>
                <li>
                    <em>PythonQtVideoPlayer/share/ui</em> → Here we will place
                    the interfaces files created with Qt Designer.
                </li>
                <li>
                    <em>PythonQtVideoPlayer/src</em> → Here will place the
                    source code of the program.
                </li>
            </ul>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot017.png">
                    <img class="thumbnail"
                         alt="Project structure"
                         src="pics/snapshots/snapshot017.png" />
                </a>
            </p>
            <p>
                Inside the <em>src</em> folder, create a file named
                <em>videoplayer.py</em> in which will put the code below, that
                is the minimum initialization code needed to run the program.
                The explanation of each line is included as comments in the
                code.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 24-24
# Import the "sys" module for passing the command line arguments to Qapplication.
import sys

# src/videoplayer.py: 31-44
# The "QtCore" module is the basis for the rest of the Qt modules, it gives access
# to all functionalities of Qt that doesn't requires the initialisation of the
# graphics system.
# The "QtGui" module gives access to all functionalities for creating the graphics
# user interface for our apps, for instance, the widgets, the dialogs, OpenGL
# graphic widgets, etc..
# The "uic" module is exclusive of PyQt. Among other things, this module can
# read and create the graphic interface from a .ui on run time, it also can
# create source code for the GUI from the .ui files.
# .ui files are XML files that defines the attributes and actions of each widget
# into the GUI.
# The phonon module give access to all multimedia functionalities, such as
# music, video, pictures and online streamming play.
from PyQt4 import QtCore, QtGui, uic, phonon
            </pre>
        </div>
        <div class="page">
            <pre class="brush: py;">
# src/videoplayer.py: 51-59
# The VideoPlayer class will be a wrapper for MainWindow.
class VideoPlayer(QtGui.QMainWindow):
    def __init__(self):
        # First, we must initialize the constructor of the ancestor class.
        QtGui.QMainWindow.__init__(self)

        # Load the GUI from videoplayer.ui. now, "self" is like as a pointer to
        # MainWindow.
        uic.loadUi('../share/ui/videoplayer.ui', self)


# src/videoplayer.py: 558-571
# Python does not uses or requires any special main block for executing the
# code. But, under some circumstances could be useful putting the codeto run
# inside of an "if" block that checks the __name__ global variable, if we are
# executing the module alone, __name__ will be equal to '__main__', otherwise,
# if the module is called by other module, __name__ will be equal to te name of
# the calling module.
# This is useful in unitary tests in which we can test each of this modules
# without executing the whole program.
if __name__ == '__main__':
    # Always, before calling a Qt object, you must create an instance of
    # QApplication, which will receive the command line argumments, initialize
    # the working environment, set some global variables, and controls the main
    # loop.
    app = QtGui.QApplication(sys.argv)

    # src/videoplayer.py: 589-593
    # Set the program name as it will appear in the task manager.
    app.setApplicationName('QtVideoPlayer')

    # Create an instance of VideoPlayer.
    videoPlayer = VideoPlayer()

    # src/videoplayer.py: 602-606
    # Show the main window of our program.
    videoPlayer.show()

    # Execute the main loop.
    app.exec_()
            </pre>
            <p>
                Now we can run the program by pressing the green arrow in Ninja
                IDE.
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot018.png">
                    <img class="thumbnail"
                         alt="Running the program"
                         src="pics/snapshots/snapshot018.png" />
                </a>
            </p>
        </div>
        <div class="page">
            <p>
                If this is the first time you run the program a dialog will
                appear to finish setting up the project properties.
            </p>
            <p>
                Go to the <strong>Project Execution</strong> tab, and setup
                <strong>Main File</strong> to <em>src/videoplayer.py</em>, and
                <strong>Python Path</strong> to the path of the Python
                executable, it can work either with Python 2 or Python 3, also
                you can set the parameters passed to your program with
                <strong>Params</strong>.
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot019.png">
                    <img class="thumbnail"
                         alt="Set main file"
                         src="pics/snapshots/snapshot019.png" />
                </a>
            </p>
            <p>
                Like as we did with Qt Designer, we can test the functionality
                of the program, although as yet we have not written the
                functionality that will have each element within the interface,
                the program will not do anything.
            </p>
        </div>
        <div class="page">
            <h1><a name="menubuttons">
                Menu Buttons
            </a></h1>
            <p>
                Let's begin creating our first code interaction with the GUI.
            </p>
            <p>
                We will create a menu in which when user press the "Add Media"
                button, the button will show the options for add new files or a
                list of URLs. The menu must look like this:
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot020.png">
                    <img class="thumbnail"
                         alt="Add media menu"
                         src="pics/snapshots/snapshot020.png" />
                </a>
            </p>
            <p>
                Each widget in the GUI can be accessed using the object name
                defined in Qt Designer. PyQt automatically creates a reference
                to each one as a member of <strong>self</strong> object.
            </p>
            <p>
                For instance, if we want to access to the "Add Media" button you
                can access to it as:
            </p>
            <pre class="brush: py;">
                self.btnAddMedia.someWidgetMethod(arg1, arg2, ...)
            </pre>
            <p>
                The code below shows how to add a menu to a button:
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 72-82
# Create a menu for adding new medias.
self.addMediaMenu = QtGui.QMenu()

# Add a new action to the menu and get a reference to it.
self.axnAddLocalFile = self.addMediaMenu.addAction(self.tr('Add local '
                                                           '&amp;File'))

self.axnAddURL = self.addMediaMenu.addAction(self.tr('Add &amp;URL'))

# Add the menu to the add media button.
self.btnAddMedia.setMenu(self.addMediaMenu)
            </pre>
            <p>
                Qt also allows you to load discs (CD, DVD, BlueRay, etc.) as
                playing medias, but we need to add specific code for each
                platform and that is out of this tutorial.
            </p>
        </div>
        <div class="page">
            <h1><a name="signalsandslots">
                Signals and slots
            </a></h1>
            <p>
                Now we will begin to receive events from the interface, that is,
                for instance when a button is pressed, you do a double click on
                the playlist, the video stops, etc..
            </p>
            <p>
                For this, we will use what in Qt are known as
                <strong>signals</strong> and <strong>slots</strong>.
            </p>
            <p>
                A <strong>signal</strong> is basically, a method to which
                objects methods that are interested on catch the event
                associated with that signal can be connected.
            </p>
            <p>
                A <strong>slot</strong> is a method that can be connected to
                a <strong>signal</strong> for catching and process the event
                associated with that <strong>signal</strong>.
            </p>
            <p>
                Any object that you want to implement <strong>signals</strong>
                and <strong>slots</strong> must inherit from
                <strong>QObject</strong> class, all Qt widgets you use do that,
                because they inherit from <strong>QWidget</strong>, and
                <strong>QWidget</strong> inherits from <strong>QObject</strong>.
                Qt widgets provides predefined signals and slots, but it is also
                possible to create a custom <strong>signals</strong> and
                <strong>slots</strong> to our own widgets.
            </p>
            <p>
                The <strong>slots</strong> are used to receive
                <strong>signals</strong>, but can also be used as normal
                functions. As an object does not know if a slot is capturing
                their signals, a slot does not know if have a signal connected
                to it. This ensures that truly independent components can be
                created with Qt.
            </p>
            <p>
                You can connect as many signals as you need to a single slot,
                and a signal can connected as many slots as you need. You can
                even connect a signal directly to another signal.
            </p>
            <p class="space"></p>
            <p>
                <a target="_blank"
                   href="http://qt-project.org/doc/latest/signalsandslots.html">
                    qt-project.org/doc/latest/signalsandslots.html
                </a>
            </p>
            <p class="space"></p>
            <p>
                To connect a slot to a signal, its general form is:
            </p>
            <pre class="brush: py;">
sender.signal.connect(slot_function)
            </pre>
            <p>
                Where <em>sender</em> is the object that sends the signal,
                <em>signal</em> is the name of the signal that you want to
                receive, <em>slot_function</em> is the slot that will receive
                the señal.
            </p>
            <p>
                For instance, if you want to receive the signal when the "About"
                button is pressed, you must do as follows:
            </p>
            <pre class="brush: py;">
self.btnAbout.clicked.connect(self.on_btnAbout_clicked)
            </pre>
            <p>
                Here the object that sends the sends the signal is
                <em>self.btnAbout</em>, the signal is <em>clicked</em>, and the
                slot is <em>self.on_ btnAbout_ clicked</em>.
            </p>
            <p>
                While not mandatory, the name of the slot should have the form
                below:
            </p>
            <pre>
on_sender_signal
            </pre>
            <p>
                You can create a slot using the form below:
            </p>
            <pre class="brush: py;">
@QtCore.Slot(argType1, argType2, ...)
def on_sender_signal(self, arg1, arg2, ...):
    ...
            </pre>
            <p>
                It may happen that an object send several signals of the same
                name but with different numbers and types of parameters. In that
                case we would like to filter those signals who match the desired
                parameters. We can filter it with decorators.
            </p>
            <p>
                Decorators are own of Python language, not Qt.
            </p>
            <p>
                A decorator is a function that receives another function as
                parameter (the function to decorate) and returns a new function
                (the decorated function).
            </p>
        </div>
        <div class="page">
            <p>
                The decorators are used generally for:
            </p>
            <ul>
                <li>
                    Modify the behavior of the original function.
                </li>
                <li>
                    Sometimes, save a lot when some code is repeated.
                </li>
                <li>
                    Parameters check.
                </li>
                <li>
                    Unitary tests.
                </li>
                <li>
                    Debugging.
                </li>
            </ul>
            <p>
                A very simple example of using decorators is as follows:
            </p>
            <pre class="brush: py;">
# Define the decorator function.
def decore_funcion(original_function):
    # Decorated function is defined inside decore function.
    # "args" and "kwargs" are the parameters that receives the original
    # function.
    def decorated_function(*args, **kwargs):
        # Here we can write all the code necessary to change the behavior of the
        # original function.
        # In this case we execute the original function and then print a
        # message.
        original_function(*args, **kwargs)
        print("This is the decorated function")

    # Return decorated function.
    return decorated_function

# Decorathe the original function.
@decore_funcion
def function():
    print("This is the original function")

function()
            </pre>
            <p>
                The output will be:
            </p>
            <pre>
> This is the original function
> This is the decorated function
            </pre>
            <p>
                For more information check this link:
            </p>
            <p class="space"></p>
            <p>
                <a target="_blank"
                   href="http://wiki.python.org/moin/PythonDecorators">
                    wiki.python.org/moin/PythonDecorators
                </a>
            </p>
            <p class="space"></p>
            <p>
                In PyQt, the slots are usually decorated, to filter the signals
                and types of data that we want to receive.
            </p>
            <p>
                PyQt also support the so called "slot autoconnection".
            </p>
            <p>
                Instead of connecting signals and slots manually as I said
                previously, you can connect by method names. For instance,
                you don't need to do this:
            </p>
            <pre class="brush: py;">
self.btnAbout.clicked.connect(self.on_btnAbout_clicked)
            </pre>
            <p>
                You just need to create the slot:
            </p>
            <pre class="brush: py;">
@QtCore.Slot()
def on_btnAbout_clicked(self):
    pass
            </pre>
            <p>
                That's all. The signal will be automatically connected to the
                slot with the <em>on_sender_signal</em> form.
            </p>
        </div>
        <div class="page">
            <p>
                Now we can create the slots to catch the GUI events..
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 290-291
@QtCore.pyqtSlot()
def on_btnClearPlayList_clicked(self):
    pass

# src/videoplayer.py: 298-299
@QtCore.pyqtSlot()
def on_btnFullscreen_clicked(self):
    pass

# src/videoplayer.py: 317-318
@QtCore.pyqtSlot()
def on_btnNext_clicked(self):
    pass

# src/videoplayer.py: 347-348
@QtCore.pyqtSlot()
def on_btnPlayPause_clicked(self):
    pass

# src/videoplayer.py: 370-371
@QtCore.pyqtSlot()
def on_btnPrevious_clicked(self):
    pass

# src/videoplayer.py: 402-403
@QtCore.pyqtSlot()
def on_btnRemoveMedia_clicked(self):
    pass

# src/videoplayer.py: 414-415
@QtCore.pyqtSlot()
def on_btnRepeatPlayList_clicked(self):
    pass

# src/videoplayer.py: 419-420
@QtCore.pyqtSlot()
def on_btnVideoFill_clicked(self):
    pass

# src/videoplayer.py: 434-435
@QtCore.pyqtSlot()
def on_btnShowPlayList_clicked(self):
    pass

# src/videoplayer.py: 439-490
@QtCore.pyqtSlot()
def on_btnShufflePlayList_clicked(self):
    pass

# src/videoplayer.py: 465-466
@QtCore.pyqtSlot(QtCore.QModelIndex)
def on_lswPlaylist_doubleClicked(self, model_index):
    pass
            </pre>
            <p>
                Also we must create and connect the slots for the menu actions.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 84-90
# When user choose an option from the menu, this action will emit the
# "triggered" signal, this signal will be connected to his respective
# slot.
self.axnAddLocalFile.triggered.\
                        connect(self.on_axnAddLocalFile_triggered)

self.axnAddURL.triggered.connect(self.on_axnAddURL_triggered)

# src/videoplayer.py: 257-258
@QtCore.pyqtSlot()
def on_axnAddLocalFile_triggered(self):
    pass

# src/videoplayer.py: 268-269
@QtCore.pyqtSlot()
def on_axnAddURL_triggered(self):
    pass
            </pre>
        </div>
        <div class="page">
            <p>
                Finally, the last way in which we can connect signals and slots
                is using Qt Designer, this way is useful when you need to
                connect them internally. In Qt Designer, press the button
                "Edit Signals/Slots", then drag and drop from the widget that
                emits the signal to the widget that will receive that signal.
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot021.png">
                    <img class="thumbnail"
                         alt="Slots&amp;signals in Qt Designer"
                         src="pics/snapshots/snapshot021.png" />
                </a>
            </p>
            <p>
                Immediately appears a dialog showing all signals and slots
                available to connect.
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot022.png">
                    <img class="thumbnail-75"
                         alt="Slots connection dialog"
                         src="pics/snapshots/snapshot022.png" />
                </a>
            </p>
            <p>
                This is very handy because we can connect all signals and slots
                without writing any code.
            </p>
        </div>
        <div class="page">
            <h1><a name="addfilesplaylist">
                Add files to the playlist
            </a></h1>
            <p>
                Now let's create a list where we will store the files to play.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 61-62
# A list of phonon.Phonon.MediaSource.
self.playlist = []
            </pre>
            <p>
                This list will be a copy of the list you will see in the
                <strong>QListWidget</strong>.
            </p>
            <p>
                The question is: Which data type will store on the list?. To
                answer this question we must first know what type of data is
                needs <strong>phonon::VideoPlayer</strong> to play media and
                with what data type we will populate the
                <strong>QListWidget</strong>.
            </p>
            <p>
                <strong>Phonon::VideoPlayer</strong> offers the following
                methods to load and play the medias:
            </p>
            <p class="space"></p>
            <p>
                <a target="_blank"
                   href="http://qt-project.org/doc/latest/phonon-videoplayer.html#load">
                    phonon.VideoPlayer.load(phonon.MediaSource source)
                </a>
            </p>
            <p>
                <a target="_blank"
                   href="http://qt-project.org/doc/latest/phonon-videoplayer.html#play">
                    phonon.VideoPlayer.play(phonon.MediaSource source)
                </a>
            </p>
            <p>
                <a target="_blank"
                   href="http://qt-project.org/doc/latest/phonon-videoplayer.html#play-2">
                    phonon.VideoPlayer.play()
                </a>
            </p>
            <p class="space"></p>
            <p>
                According to the official Qt documentation, we can load a medium
                with
                <strong>phonon.Videoplayer.load(phonon.MediaSource)</strong> and
                then play with <strong>phonon.Videoplayer.play()</strong>, or we
                can load and play the media directly with
                <strong>phonon.Videoplayer.load(phonon.MediaSource)</strong>,
                in both cases the parameter required to play the media is
                <a target="_blank"
                   href="http://qt-project.org/doc/latest/phonon-mediasource.html">
                    phonon.MediaSource
                </a>.
            </p>
            <p>
                Now let's see what parameters need <strong>phonon.MediaSource</strong>:
            </p>
            <p class="space"></p>
            <p>
                <a target="_blank"
                   href="http://qt-project.org/doc/latest/phonon-mediasource.html#MediaSource">
                    phonon.MediaSource.MediaSource(QtCore.QString fileName)
                </a>
            </p>
            <p>
                <a target="_blank"
                   href="http://qt-project.org/doc/latest/phonon-mediasource.html#MediaSource-3">
                    phonon.MediaSource.MediaSource(QtCode.QUrl url)
                </a>
            </p>
            <p class="space"></p>
            <p>
                We only want to load files and URLs. In the first constructor,
                we use the file path to play, the variable must be of
                <strong>QtCore.QString</strong>, and thats equivalent to the
                <em>str</em> type of Python. In the second constructor we use
                the web address to play it, the variable is of
                <strong>QtCore.QUrl</strong> type which in turn is initialized
                with a <em>str</em> type parameter.
            </p>
            <p>
                Now let's see what methods offers us
                <strong>QListWidget</strong> to add items to the list.
            </p>
            <p class="space"></p>
            <p>
                <a target="_blank"
                   href="http://qt-project.org/doc/latest/qlistwidget.html#addItems">
                    QtGui.QListWidget.addItems(QStringList labels)
                </a>
            </p>
            <p class="space"></p>
            <p>
                A <strong>QStringList</strong> is nothing more than a list of
                strings, a variable of <em>list</em> type with elements of
                <em>str</em> type.
            </p>
            <p>
                So to add medias to the playlist we will use a list of strings,
                that will contains the names of files and URLs.
            </p>
            <p>
                Therefore, <em>self.playlist</em> will store elements of
                <strong>phonon.MediaSource</strong> type.
            </p>
            <p>
                To add files to playlist, add the following code to
                <em>on_axnAddLocalFile_triggered</em>:
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 257-266
@QtCore.pyqtSlot()
def on_axnAddLocalFile_triggered(self):
    # Open the file selection dialog.
    # The first parameter stablish MainWindow as the parent of the dialog,
    # and the second sets the dialog title.
    filenames = QtGui.QFileDialog.\
                        getOpenFileNames(self, self.tr('Add local files'))

    # Add the filenames to the file names to the playlist.
    self.addMedias(filenames)
            </pre>
        </div>
        <div class="page">
            <p>
                Import the <em>os</em> module:
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 23-23
import os
            </pre>
            <p>
                Define the <em>self.addMedias</em> method:
            </p>
            <pre class="brush: py;">
# scr/videoplayer.py: 228-255
def addMedias(self, medias=[]):
    # Sort the media list alphabetically.
    medias.sort()

    # We assume that we aren't playing any media.
    play = False

    # If user chose at least one media...
    if medias != []:
        # If the playlist is empty, and there aren't a media playing.
        if self.playlist == []:
            play = True

        # Populate the playlist.
        for media in medias:
            if media.startswith('http://') or media.startswith('mms://'):
                self.playlist += [phonon.Phonon.\
                                            MediaSource(QtCore.QUrl(media))]
            else:
                self.playlist += [phonon.Phonon.MediaSource(media)]

        self.updatePlayList()

    # If the playlist has been loaded and there aren't a media playing...
    if play and \
        self.vdpVideo.mediaObject().state() != phonon.Phonon.PlayingState:
        # play it.
        self.on_btnPlayPause_clicked()
            </pre>
            <p>
                Now, define the <em>self.updatePlayList</em> method, this will
                update the playlist when the user adds a new media.
            </p>
            <pre class="brush: py;">
# scr/videoplayer.py: 135-155
# This method updates the playlist when user adds a new media.
def updatePlayList(self):
    # Create an empty playlist.
    playlist = []

    # For each media to play...
    for source in self.playlist:
        # If the media is a local file...
        if source.type() == phonon.Phonon.MediaSource.LocalFile:
            # Just add the name of the file without the path.
            playlist += [os.path.basename(str(source.fileName()))]
        # If the media is an URL...
        elif source.type() == phonon.Phonon.MediaSource.Url:
            # Add the URL to the list.
            playlist += [str(source.url().toString())]

    # Remove all items in QListWidget
    self.lswPlaylist.clear()

    # add the new playlist.
    self.lswPlaylist.addItems(playlist)
            </pre>
        </div>
        <div class="page">
            <h1><a name="playfiles">
                Play files
            </a></h1>
            <p>
                Now, let's add the code to play the media files. First, we need
                to connect <strong>phonon::VideoPlayer</strong> with the volume
                and time sliders.
            </p>
            <p>
                Add the following code to <em>__ init__()</em>:
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 106-111
# We connect the audio slider to the video player.
# Doing this we can control the time position of the video or music.
self.sldVolumeSlider.setAudioOutput(self.vdpVideo.audioOutput())

# Connect the volume slider to the video player.
self.sldSeekSlider.setMediaObject(self.vdpVideo.mediaObject())
            </pre>
            <p>
                Add the following code to play a media by double clicking on an
                item from the list.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 462-469
# When the user do a doble click on an item in QListWidget, it emits the
# "doubleClicked" signal, and pass a reference to the clicked item as
# parameter, which is a QtGui.QListWidgetItem type.
@QtCore.pyqtSlot(QtCore.QModelIndex)
def on_lswPlaylist_doubleClicked(self, model_index):
    # If the playlist is empty, do nothing.
    if self.playlist == []:
        return

    # src/videoplayer.py: 478-478
    self.vdpVideo.play(self.playlist[self.lswPlaylist.row(model_index)])
            </pre>
            <p>
                Now you can load some files to the playlist and double click on
                any of them to play, also you can control the volume and playing
                time, and can also stop the playback with the <em>Stop</em>
                button.
            </p>
        </div>
        <div class="page">
            <h1><a name="timers">
                Using timers
            </a></h1>
            <p>
                Now we will use the <strong>QLabel</strong> that we had created
                in Qt Designer to display the current playing time, for it will
                create a timer that every so often pick the current timestamp of
                the media.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 117-129
# Create a timer for update the playtime of the current media and
# the mouse status.
self.tmrTimer = QtCore.QTimer(self)

# The timer will emit the "timeout" signal every a quarter of second.
self.tmrTimer.setInterval(250)

# When it reaches the stablished time, the timer will emit the "timeout"
# signal and it respective slot is called.
self.tmrTimer.timeout.connect(self.on_tmrTimer_timeout)

# Starts the timer.
self.tmrTimer.start()
            </pre>
            <p>
                Finally we add the <em> on_tmrTimer_timeout</em> slot:
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 480-508
@QtCore.pyqtSlot()
def on_tmrTimer_timeout(self):
    # First update the playtime.

    # Gte the current time mark,
    currentTime = self.vdpVideo.currentTime()

    if currentTime == 0:
        # If the current playtime is 0 seconds, clear the label text.
        self.lblTime.setText('')
    else:
        # Get the total playtime of the media.
        totalTime = self.vdpVideo.totalTime()

        # If the total playtime is less than 1 hour, just show minutes
        # and seconds.
        tFormat = 'mm:ss' if totalTime &lt; 3600000 else 'hh:mm:ss'

        # We use Qtime for time convertions.
        currentTimeH = QtCore.QTime()

        # Convert times to a human readable strings.
        ct = currentTimeH.addMSecs(currentTime).toString(tFormat)

        totalTimeH = QtCore.QTime()
        tt = totalTimeH.addMSecs(totalTime).toString(tFormat)

        # Set time to label.
        self.lblTime.setText(ct + '/' + tt)
            </pre>
        </div>
        <div class="page">
            <h1><a name="fullscreen">
                Go fullscreen
            </a></h1>
            <p>
                There is nothing better than seeing a movie on a screen as large
                as possible, for it we will add options to put the window in
                fullscreen mode.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 298-315
@QtCore.pyqtSlot()
def on_btnFullscreen_clicked(self):
    # If the window is currently in fullscreen mode...
    if self.isFullScreen():
        # show it in normal mode.
        self.showNormal()

        # Show the playlist.
        self.dckPlayList.show()
    else:
        # show it in fullscreen mode.
        self.showFullScreen()

        # Hide the playlist.
        self.dckPlayList.hide()

        # Hide the mouse cursor.
        self.setCursor(QtGui.QCursor(QtCore.Qt.BlankCursor))
            </pre>
            <p>
                In fullscreen mode we must hide the mouse cursor, the controls
                bar, and the playlist.
            </p>
            <p>
                If the user moves the mouse, it must be unhidden and stay
                visible arround 1 second, to hide it again.
            </p>
            <p>
                When the mouse approaches to the controls bar area, the controls
                bar will be unhidden, When the mouse leaves the controls bar
                area, the controls bar will be hidden again.
            </p>
            <p>
                When user exits from full fullscreen mode, the mouse cursor, the
                controls bar, and the playlist will be shown again.
            </p>
            <p>
                First, initialize the current mouse position and time.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 131-133
# Stores a reference to the position and the current time.
self.mousePos0 = QtGui.QCursor.pos()
self.mouseT0 = QtCore.QTime.currentTime()
            </pre>
        </div>
        <div class="page">
            <p>
                Then, add the following code to <em>on_tmrTimer_timeout()</em>:
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 510-557
# Now update the mouse status.

# The window is in fullscreen mode...
if self.isFullScreen():
    # Update the current mouse time and position.
    mousePos = QtGui.QCursor.pos()
    mouseT = QtCore.QTime.currentTime()

    # Normally, when the program is in fullscreen mode, the mouse must
    # be hidden until user move it.
    if (mousePos != self.mousePos0 and \
        self.cursor().shape() == QtCore.Qt.BlankCursor) or \
        self.wdgVideoControls.isVisible():
        self.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))

        # Reset the time count for calculating the mouse moving time.
        self.mouseT0 = QtCore.QTime.currentTime()
    # If user stops moving the mouse, it must stay visible at least some
    # seconds.
    elif self.cursor().shape() == QtCore.Qt.ArrowCursor and \
            self.mouseT0.secsTo(mouseT) > 1:
        self.setCursor(QtGui.QCursor(QtCore.Qt.BlankCursor))

    # Update the current mouse position.
    self.mousePos0 = mousePos

    # Convert the global mouse position in the screen to the window
    # local coordinates. And get the coordinate for Y axis.
    mouseY = self.mapFromGlobal(mousePos).y()

    # If the mouse approaches to the position in which must be the
    # controls bar, it must be unhidden.
    if mouseY &lt; self.height() and \
        mouseY > self.height() - self.wdgVideoControls.height():
        if self.wdgVideoControls.isHidden():
            self.wdgVideoControls.show()
    # otherwise it must stay hidden.
    elif self.wdgVideoControls.isVisible():
        self.wdgVideoControls.hide()
# The window is in normal mode...
else:
    # If the mouse cursor is hidden, show it.
    if self.cursor().shape() == QtCore.Qt.BlankCursor:
        self.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))

    # Show playlist.
    if self.wdgVideoControls.isHidden():
        self.wdgVideoControls.show()
            </pre>
            <p>
                Also, we can add code to set the window in fullscreen mode when
                user double click en the video.
            </p>
            <p>
                We will made use of the
                <a target="_blank"
                   href="http://qt-project.org/doc/latest/qwidget.html#mouseDoubleClickEvent">
                    mouseDoubleClickEvent()
                </a> protected function to catch that event.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 157-161
# mouseDoubleClickEvent is a protected method which called when user does
# doble click in the GUI and this event isn't catched by anyother widget.
def mouseDoubleClickEvent(self, event):
    # Always, before process the event, we must send a copy of it to the
    # ancestor class.
    QtGui.QMainWindow.mouseDoubleClickEvent(self, event)

    # Go to fullscreen mode or exit from it.
    self.on_btnFullscreen_clicked()
            </pre>
        </div>
        <div class="page">
            <h1><a name="aspectratio">
                Video aspect ratio
            </a></h1>
            <p>
                Some people prefer to see the video in its original dimensions,
                which means that when the proportions of the video do not match
                the proportions of the window or screen, the video shows with
                black bars on the sides, but others prefer the video to fills
                the video widget regardless of size, which means there may be a
                slight disproportionality in the image, the image is elongated.
            </p>
            <p>
                To change the video aspect ratio, use the following code:
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 419-432
@QtCore.pyqtSlot()
def on_btnVideoFill_clicked(self):
    # If the btnVideoFill button is checked, the video will be stretched to
    # fill the entire video widget.
    # otherwise, the video will be preserve it's aspect ratio.
    if self.vdpVideo.videoWidget().aspectRatio() == \
        phonon.Phonon.VideoWidget.AspectRatioWidget:
        self.vdpVideo.videoWidget().\
                        setAspectRatio(phonon.Phonon.VideoWidget.\
                                                    AspectRatioAuto)
    else:
        self.vdpVideo.videoWidget().\
                        setAspectRatio(phonon.Phonon.VideoWidget.\
                                                    AspectRatioWidget)
            </pre>
        </div>
        <div class="page">
            <h1><a name="playpause">
                Play and pause
            </a></h1>
            <p>
                Now add the functionality to play and pause the media.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 347-368
@QtCore.pyqtSlot()
def on_btnPlayPause_clicked(self):
    # If we are playing a media...
    if self.vdpVideo.mediaObject().state() == phonon.Phonon.PlayingState:
        # pause it.
        self.vdpVideo.pause()
    # If the current media is paused...
    elif self.vdpVideo.mediaObject().state() == phonon.Phonon.PausedState:
        # play it.
        self.vdpVideo.play()
    # If there aren't a selected media in the playlist...
    elif self.lswPlaylist.currentRow() &lt; 0:
        # Select the first item in the list.
        self.lswPlaylist.setCurrentRow(0, QtGui.QItemSelectionModel.\
                                                SelectCurrent)

        # call to the on_lswPlayList_doubleClicked slot to play the media.
        self.on_lswPlaylist_doubleClicked(self.lswPlaylist.currentItem())
    # If there are at least one item selected...
    else:
        # call to the on_lswPlayList_doubleClicked slot to play it.
        self.on_lswPlaylist_doubleClicked(self.lswPlaylist.currentItem())
            </pre>
            <p>
                Also modify the <em>on_lswPlaylist_doubleClicked</em> slot,
                because the parameters with which we will call this slot are
                different from how we had defined.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 462-478
# When the user do a doble click on an item in QListWidget, it emits the
# "doubleClicked" signal, and pass a reference to the clicked item as
# parameter, which is a QtGui.QListWidgetItem type.
@QtCore.pyqtSlot(QtCore.QModelIndex)
def on_lswPlaylist_doubleClicked(self, model_index):
    # If the playlist is empty, do nothing.
    if self.playlist == []:
        return

    try:
        # Get the index of model_view, use it to obtain the corresponding
        # MediaSource, and then play it.
        # Here model_index is of the type QtGui.QListWidgetItem.
        self.vdpVideo.play(self.playlist[model_index.row()])
    except:
        # Here model_index is of the type QtGui.QModelIndex.
        self.vdpVideo.play(self.playlist[self.lswPlaylist.row(model_index)])
            </pre>
        </div>
        <div class="page">
            <h1><a name="shufflerepeat">
                Shuffle and repeat the playlist
            </a></h1>
            <p>
                No one multimedia player who prides of it, can't lack the
                classic options of repeat and shuffle the playlist.
            </p>
            <p>
                We will add some variables in <em>__init__</em> to control
                these states.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 64-70
# This will be a copy of the playlist before shuffling.
self.playlistTmp = []

# This flag indicates that when the program reach the end of the
# playlist, start playing all the playlist again from beginning, or if
# we are at the beginning of the playlist, go to the last media.
self.repeat = False
            </pre>
            <p>
                First let's see how to shuffle the playlist, we must add the
                <em>random</em> module:
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 28-29
# "ramdom" module is used for mezclar playlist shuffling.
import random
            </pre>
            <p>
                And finally add the following code:
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 439-460
@QtCore.pyqtSlot()
def on_btnShufflePlayList_clicked(self):
    # If the playlist is empty...
    if self.playlistTmp == []:
        # The temporal playlist will be a copy of the current playlist.
        self.playlistTmp = self.playlist[:]

        # get number of items in the list.
        items = len(self.playlist)

        # Sort the list randomly.
        self.playlist = [self.playlist.pop(
                            random.randint(0, len(self.playlist) - 1))
                            for index in range(items)]
    else:
        # Return the playlist to it's original state.
        self.playlist = self.playlistTmp[:]

        # and clear the temporal playlist.
        self.playlistTmp = []

    self.updatePlayList()
            </pre>
            <p>
                The <em>self.repeat</em> flag will be needed when we activate
                the forward and backward functions. So now just add the
                functionality to modify the flag with the following code:
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 414-417
@QtCore.pyqtSlot()
def on_btnRepeatPlayList_clicked(self):
    # Toggle on/off repeat mode.
    self.repeat = not self.repeat
            </pre>
        </div>
        <div class="page">
            <h1><a name="seekmedia">
                Move forward and backward the medias
            </a></h1>
            <p>
                Now add the functionality to play the following media.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 317-345
@QtCore.pyqtSlot()
def on_btnNext_clicked(self):
    if self.playlist == []:
        return

    # Get a reference of the current selected media.
    curIndex = self.lswPlaylist.currentRow()

    # If the current selected media index is less than the size of the
    # playlist...
    if curIndex + 1 &lt; len(self.playlist):
        # increase the index of the current media.
        index = curIndex + 1
    # If the current selected media index is greater than the size of the
    # playlist...
    else:
        # If the repeat mode isn't selected then do nothing.
        if not self.repeat:
            return

        # otherwise, the next media to play will be the first in the list.
        index = 0

    # Select the next media in the playlist
    self.lswPlaylist.setCurrentRow(index,
                                    QtGui.QItemSelectionModel.SelectCurrent)

    # and play it.
    self.on_lswPlaylist_doubleClicked(self.lswPlaylist.item(index))
            </pre>
            <p>
                We do the same to switch to the previous media.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 370-400
@QtCore.pyqtSlot()
def on_btnPrevious_clicked(self):
    if self.playlist == []:
        return

    # Get a reference of the current selected media.
    curIndex = self.lswPlaylist.currentRow()

    # Sí cur_index - 1 es mayor o igual que el primer ítem de la lista de
    # reproducción...
    # If the current selected media index is greater or equal to the first
    # index of the playlist...
    if curIndex - 1 >= 0:
        # decrease the index of the current media.
        index = curIndex - 1
    # If the current selected media index is less than the first item of the
    # playlist...
    else:
        # If the repeat mode isn't selected then do nothing.
        if not self.repeat:
            return

        # otherwise, the next media to play will be the last in the list.
        index = len(self.playlist) - 1

    # Select the previous media in the playlist
    self.lswPlaylist.setCurrentRow(index,
                                    QtGui.QItemSelectionModel.SelectCurrent)

    # and play it.
    self.on_lswPlaylist_doubleClicked(self.lswPlaylist.item(index))
            </pre>
        </div>
        <div class="page">
            <h1><a name="clearplaylist">
                Removing the medias from playlist
            </a></h1>
            <p>
                This is the code to delete only the selected items in the list.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 402-412
@QtCore.pyqtSlot()
def on_btnRemoveMedia_clicked(self):
    # For each selected media...
    for media in self.lswPlaylist.selectedItems():
        try:
            # get it's index and remove it from the playlist.
            del self.playlist[self.lswPlaylist.row(media)]
        except:
            pass

    self.updatePlayList()
            </pre>
            <p>
                And this is the code to clear the entire playlist.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 290-296
@QtCore.pyqtSlot()
def on_btnClearPlayList_clicked(self):
    # Clear the original and temporal playlist.
    self.playlist = []
    self.playlistTmp = []

    self.updatePlayList()
            </pre>
        </div>
        <div class="page">
            <h1><a name="addurlplaylist">
                Add URLs to the playlist
            </a></h1>
            <p>
                Vamos a crear un diálogo personalizado en el que el usuario
                puede añadir varias direcciones URL al mismo tiempo.
            </p>
            <p>
                Go to Qt Designer and create a <strong>Dialogs With Buttons
                Bottom</strong> dialog that may looks like this:
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot023.png">
                    <img class="thumbnail-75"
                         alt="Add URL dialog"
                         src="pics/snapshots/snapshot023.png" />
                </a>
            </p>
            <p>
                We can add or remove buttons to the
                <strong>QDialogButtonBox</strong> from the Property Editor with
                the <strong>standardButtons</strong> property.
            </p>
            <p>
                In our case, we only need the <em>Ok</em> and <em>Cancel</em>
                buttons.
            </p>
            <p>
                Also, <strong>modal</strong> dialog property must be marked, so
                that when called, blocks the window that called it.
            </p>
            <p>
                We will add it to the <em>share/ui</em> folder with
                <em>addurl.ui</em> as name and also create the file
                <em>src/addurl.py</em>.
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot024.png">
                    <img alt="Project structure"
                         class="thumbnail"
                         src="pics/snapshots/snapshot024.png" />
                </a>
            </p>
        </div>
        <div class="page">
            <p>
                In <em>addurl.py</em> add the following code, I will comment
                only new parts, since most have already been explained above.
            </p>
            <pre class="brush: py;">
# src/addurl.py: 23-74
import sys

from PyQt4 import QtCore, QtGui, uic


class AddURL(QtGui.QDialog):
    def __init__(self, parent=None):
        QtGui.QDialog.__init__(self, parent)

        uic.loadUi('../share/ui/addurl.ui', self)

        # We store all URLs added by the user in a list.
        self.urls = []

        # When the user click the "Ok" button..
        self.bbxAcceptCancel.accepted.connect(self.on_bbxAcceptCancel_accepted)

        # When the user click the "Cancel" button..
        self.bbxAcceptCancel.rejected.connect(self.on_bbxAcceptCancel_rejected)

    @QtCore.pyqtSlot()
    def on_bbxAcceptCancel_accepted(self):
        # Read the text from QPlainTextEdit.
        # Each line is an URL.
        # I don't add url checking for no complicating the code.
        self.urls = str(self.txtURLList.toPlainText()).split()

        # Sort the URL list.
        self.urls.sort()

        # Close the dialog and return 0, this means that the user accepted the
        # action.
        self.done(0)

    @QtCore.pyqtSlot()
    def on_bbxAcceptCancel_rejected(self):
        # Close the dialog and return 1, this means that the user cancelled the
        # action.
        self.done(1)


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)

    translator = QtCore.QTranslator(app)
    locale = QtCore.QLocale.system().name()
    translator.load(locale + '.qm', '../share/ts')
    app.installTranslator(translator)

    add_url = AddURL()
    add_url.show()
    app.exec_()
            </pre>
            <p>
                Add the module to <em>videoplayer.py</em>.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 46-47
# Import the modules with the dialog of our program.
import addurl
            </pre>
        </div>
        <div class="page">
            <p>
                Finally, define the <em>on_axnAddURL_triggered</em> slot.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 267-279
@QtCore.pyqtSlot()
def on_axnAddURL_triggered(self):
    # Create an instance for the "Add URL" dialog.
    addURLDlg = addurl.AddURL(self)

    # Open the dialog.
    # The program execution will stop here until user close the dialog.
    addURLDlg.exec_()

    if addURLDlg.result() == 0:
        # Add the URLs to the playlist.
        self.addMedias(addURLDlg.urls)
            </pre>
            <p>
                Finally the result will be this:
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot025.png">
                    <img class="thumbnail"
                         alt="Final Add URL dialog"
                         src="pics/snapshots/snapshot025.png" />
                </a>
            </p>
        </div>
        <div class="page">
            <h1><a name="about">
                The "About" dialog
            </a></h1>
            <p>
                Finally, we will create two <strong>About</strong> dialogs, one
                for our program and other for Qt. First, start creating a new
                menu for the <strong>About</strong> button.
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 92-104
# Create a menu for the "About" button.
self.aboutMenu = QtGui.QMenu()

# Add the actions to the menu and get a reference to it.
self.axnAbout = self.aboutMenu.addAction(self.tr('About this &amp;Program'))
self.axnAboutQt = self.aboutMenu.addAction(self.tr('About &amp;Qt'))

# Add the menu to the "About" button.
self.btnAbout.setMenu(self.aboutMenu)

# Connect the signals.
self.axnAbout.triggered.connect(self.on_axnAbout_triggered)
self.axnAboutQt.triggered.connect(self.on_axnAboutQt_triggered)
            </pre>
            <p>
                And connect the slot.
            <p>
            <pre class="brush: py;">
# src/videoplayer.py: 281-282
@QtCore.pyqtSlot()
def on_axnAbout_triggered(self):
    pass

# src/videoplayer.py: 286-287
@QtCore.pyqtSlot()
def on_axnAboutQt_triggered(self):
    pass
            </pre>
            <p>
                Now, we will design the about dialog for our program.
            </p>
            <p>
                The information that will contain the dialog depends on the
                likes and needs of the software designer. For example, we may
                place information of the license, any image identifying the
                program, information on how to use the program, commands, etc..
            </p>
            <p>
                In our case with something simple will be enough, anyway, is
                just an example.
            </p>
            <p>
                In Qt Designer, create a <strong>Dialog With Buttons
                Bottom</strong> as below:
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot026.png">
                    <img class="thumbnail-75"
                         alt="Designing About dialog"
                         src="pics/snapshots/snapshot026.png" />
                </a>
            </p>
            <p>
                Connect the <strong>accepted</strong> signal emitted by
                <strong>buttonBox</strong>, to the <strong>accept</strong> slot
                belonging to the dialogue (connection to ground).
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot027.png">
                    <img class="thumbnail"
                         alt="Connecting signals to ground"
                         src="pics/snapshots/snapshot027.png" />
                </a>
            </p>
        </div>
        <div class="page">
            <p>
                Create a new file called <em>aboutdlg.py</em> and put the
                following code, there is no need to explain the code, because
                this is the basic template:
            </p>
            <pre class="brush: py;">
# src/aboutdlg.py: 23-45
import sys

from PyQt4 import uic, QtGui, QtCore


class About(QtGui.QDialog):
    def __init__(self, parent=None):
        QtGui.QDialog.__init__(self, parent)

        uic.loadUi('../share/ui/aboutdlg.ui', self)


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)

    translator = QtCore.QTranslator(app)
    locale = QtCore.QLocale.system().name()
    translator.load(locale + '.qm', '../share/ts')
    app.installTranslator(translator)

    about = About()
    about.show()
    app.exec_()
            </pre>
            <p>
                The project structure must be like this:
            </p>
            <p class="thumbnail">
                <a target="_blank"
                   href="pics/snapshots/snapshot028.png">
                    <img class="thumbnail"
                         alt="Project structure"
                         src="pics/snapshots/snapshot028.png" />
                </a>
            </p>
            <p>
                And finally add the following code to call the dialog when the
                user presses the <strong>About</strong> button:
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 281-284
@QtCore.pyqtSlot()
def on_axnAbout_triggered(self):
    about = aboutdlg.About(self)
    about.exec_()
            </pre>
            <p>
                Qt provides his own <strong>About</strong> dialog that shows the
                current Qt version used to run the program. You can call this
                dialog with the <strong>aboutQt</strong> static method provided
                by the <strong>QMessageBox</strong> class.
            </p>
            <p class="space"></p>
            <p>
                <a target="_blank"
                   href="http://qt-project.org/doc/qt-4.8/qmessagebox.html#aboutQt">
                    QMessageBox.aboutQt(QWidget parent, QString title)
                </a>
            </p>
            <p class="space"></p>
            <p>
                Add the following code to call this dialog:
            </p>
            <pre class="brush: py;">
# src/videoplayer.py: 286-288
@QtCore.pyqtSlot()
def on_axnAboutQt_triggered(self):
    QtGui.QMessageBox.aboutQt(self, self.tr('About Qt'))
            </pre>
        </div>
        <div class="page">
            <h1><a name="stylesheets">
                Using stylesheets
            </a></h1>
            <p>
                In progress
            </p>
        </div>
        <div class="page">
            <h1><a name="internationalisation">
                Internationalisation
            </a></h1>
            <p>
                In progress
            </p>
            <p>
http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/i18n.html

In the root directory of the project run:

    pylupdate4 Webcamoid.pro

Then a _contents/ts/lang.ts_ will be created. Translate that file to your language using Qt Linguist, and change _lang.ts_ to your code language, for instance, if you speak Spanish call it _es.ts_. Then run the following command:

    lrelease contents/ts/lang.ts
            </p>
        </div>
        <div class="page">
            <h1><a name="relatedlinks">
                Links of interest
            </a></h1>
            <ul>
                <li>
                    The <a target="_blank"
                           href="http://qt-project.org/">
                            Qt Developer Network
                        </a>,
                    is the official and main Qt developers community.
                </li>
                <li>
                    The official documentation for
                    <a target="_blank"
                       href="http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/index.html">
                        PyQt
                    </a>
                    where you can found many examples of this binding.
                </li>
            </ul>
        </div>
    </body>
</html>
